/**
 * KeyDistributionCenter.cpp
 * Greg Steltenpohl & Cory Bryant
 */

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <pthread.h>

#include "KeyDistributionCenter.h"
#include "PracticalSocket.h"

using namespace std;

struct ThreadStart {
	TCPSocket* socket;
	KeyDistributionCenter* kdc;
};

/*
 * KeyDistributionCenter threadMain.
 * Main program of a thread.
 * Modified from PracticalSocket's TCPEchoServer-Thread.cpp example.
 */
void* threadMain(void* threadStart) {
	// Make sure thread resources are deallocated upon return.
	pthread_detach(pthread_self());
	ThreadStart* threadStartPointer = (ThreadStart*) threadStart;
	ThreadStart threadStartStruct = *threadStartPointer;
	TCPSocket* socket = threadStartStruct.socket;
	KeyDistributionCenter* kdc = threadStartStruct.kdc;
	// Extract socket file descriptor from argument.
	(*kdc).handleTCPRequest(socket);
	delete(TCPSocket*) socket;
	return NULL;
}

/*
 * Main function pulls data from file and user, threaded sockets.
 * Modified from PracticalSocket's TCPEchoServer-Thread.cpp example.
 */
int main(int argc, char* argv[]) {
	ifstream starterFile ("Start.txt"); // Starter file reading from.
	char* line = new char[256]; // Line in file.
	memset(line, 0, 256);
	char* token = new char[256]; // First token of line.
	memset(token, 0, 256);
	char* delimiter = "="; // Tokenizer delimiter.
	int port = 0;
	KeyDistributionCenter* kdc;
	char* keyA;
	Client* client;
	int numberOfConnections;
	int j;
	char* serverNumber;
	char* keyS;
	char* idA;
	char* keyB;
	char* idB;
	Server* server;
	// Get from file.
	if (starterFile.is_open()) {
		// Get next line with "encryption".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "encryption"));
		// Get encryption.
		int encryption = atoi(strtok(NULL, delimiter));
		kdc = new KeyDistributionCenter(encryption);
		// Get next line with "port".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "port"));
		// Get port.
		port = atoi(strtok(NULL, delimiter));
		// Get next line with "numberOfClients".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "numberOfClients"));
		// Get numberOfClients.
		int numberOfClients = atoi(strtok(NULL, delimiter));
		// Get each client for idClientMap.
		for (int i = 0; i < numberOfClients; i++) {
			// Get next line with "keyA"
			keyA = new char[INNER_BUFFER_LENGTH];
			memset(keyA, 0, INNER_BUFFER_LENGTH);
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "keyA"));
			// Get keyA.
			sprintf(keyA, "%s", strtok(NULL, delimiter));
			// Create a new client.
			client = new Client(keyA);
			// Get next line with "numberOfConnections".
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "numberOfConnections"));
			// Get numberOfConnections.
			numberOfConnections = atoi(strtok(NULL, delimiter));
			// Get each connection for connections map.
			for (j = 0; j < numberOfConnections; j++) {
				// Get next line with "serverNumber"
				serverNumber = new char[8];
				memset(serverNumber, 0, 8);
				do {
					starterFile.getline(line, 256);
					token = strtok(line, delimiter);
				} while (strcmp(token, "serverNumber"));
				// Get serverNumber.
				sprintf(serverNumber, "%s", strtok(NULL, delimiter));
				// Get next line with "keyS"
				keyS = new char[INNER_BUFFER_LENGTH];
				memset(keyS, 0, INNER_BUFFER_LENGTH);
				do {
					starterFile.getline(line, 256);
					token = strtok(line, delimiter);
				} while (strcmp(token, "keyS"));
				// Get keyS.
				sprintf(keyS, "%s", strtok(NULL, delimiter));
				// Add connection to client's connection map.
				client->addConnection(serverNumber, keyS);
			}
			// Enter idA for the client.
			idA = new char[INNER_BUFFER_LENGTH];
			memset(idA, 0, INNER_BUFFER_LENGTH);
			cout << "Please enter the idA for the " << i << "th client; the id is generated by the client.\n>";
			cin >> idA;
			// Add client to kdc map.
			kdc->addClient(idA, client);
		}
		// Get next line with "numberOfServers".
		do {
			starterFile.getline(line, 256);
			token = strtok(line, delimiter);
		} while (strcmp(token, "numberOfServers"));
		// Get numberOfServers.
		int numberOfServers = atoi(strtok(NULL, delimiter));
		// Get each server for idServerMap.
		for (int i = 0; i < numberOfServers; i++) {
			// Get next line with "serverNumber"
			serverNumber = new char[8];
			memset(serverNumber, 0, 8);
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "serverNumber"));
			// Get server number.
			sprintf(serverNumber, "%s", strtok(NULL, delimiter));
			// Get next line with "keyB"
			keyB = new char[INNER_BUFFER_LENGTH];
			memset(keyB, 0, INNER_BUFFER_LENGTH);
			do {
				starterFile.getline(line, 256);
				token = strtok(line, delimiter);
			} while (strcmp(token, "keyB"));
			// Get keyB.
			sprintf(keyB, "%s", strtok(NULL, delimiter));
			// Enter idB for the server.
			idB = new char[INNER_BUFFER_LENGTH];
			memset(idB, 0, INNER_BUFFER_LENGTH);
			cout << "Please enter the idB for the " << i << "th server; the id is generated by the server.\n>";
			cin >> idB;
			// Create a new server.
			server = new Server(serverNumber, keyB);
			// Add server to kdc map.
			kdc->addServer(idB, server);
		}
		// Close file.
		starterFile.close();
	
	} else {
		cout << "FILE ERROR" << endl;
		exit(1);
	}
	try {
		TCPServerSocket serverSocket(port); // Socket descriptor for server.
		for (;;) { // Run forever.
			// Create separate memory for client argument.
			TCPSocket* clientSocket = serverSocket.accept();
			// Create client thread.
			pthread_t threadID; // Thread ID from pthread_create().
			ThreadStart threadStart;
			threadStart.socket = clientSocket;
			threadStart.kdc = kdc;
			if (pthread_create(&threadID, NULL, threadMain, (void*) &threadStart) != 0) {
				cerr << "Unable to create thread" << endl;
				exit(1);
			}
		}
	} catch (SocketException &e) {
		cerr << e.what() << endl;
		exit(1);
	}
	return 0;
}
